﻿using Lails.MQClient.RabbitMQ.Net6;

using RabbitMQ.Client;

using System.Text;

class Program
{
    static void Main(string[] args)
    {
        多个消费都绑定一个队列();

        // Subscribe();

        // Publish();

        Console.Read();
    }

    private static void 多个消费都绑定一个队列()
    {
        var exchange = "local-test-exchange";
        var queue = "local-test-queue";
        var host = "127.0.0.1";
        var port = 5672;
        var userName = "test";
        var password = "test";
        var routeKey = "local.test.msg";

        var routeKeys = new List<string>();
        routeKeys.Add("local.test.msg");
        routeKeys.Add("local.test.msg.*");
        routeKeys.Add("local.test.msg.#");

        var pushRouteKeys = new List<string>();
        pushRouteKeys.Add("local.test.msg");
        pushRouteKeys.Add("local.test.msg.a");
        pushRouteKeys.Add("local.test.msg.b");
        pushRouteKeys.Add("local.test.msg.aa.bb");
        pushRouteKeys.Add("local.test.msg.aabb.cc.ee");
        pushRouteKeys.Add("local.test.msg.345.ee");

        // Direct Exchange（直连交换机Direct Exchange（完全匹配路由key），RouteKey必须完全匹配才会被队列接收，否则该消息会被抛弃；）
        // 情景1：消费者订阅：相同交换机名称、路由Key相同，队列名称不同；生产者提交消息（交换机名称、路由key与消费者订阅时用的相同）；结果：消费者都接收到相同数量的消息；
        // 情景2：消费都订阅：相同交换机名称、路由key相同、队列名称相同；生产者提交消息（交换机名称、路由key与消费都订阅时用的相同）；结果：消息被均摊发送到消费者，起到负载均衡的作用；
        // 情景3：消费者订阅：路由key相同，交换机名称与队列名称不同；生产者提交消息（交换机名称与消费者订阅时用的不同，但路由key相同）；
        //       结果：消费都都接收不到消息，原因，队列需要与交换机做关联，交换机间没有数据交互，生产者提交的消息后，
        //       首先会到指定的交换机，再由交换机分配到符合规则的队列，最终队列将消息发送给消费者；

        // Topic Exchange（主题交换机Topic Exchange（匹配路由规则的交换机），Exchange将RouteKey和某Topic进行模糊匹配）
        // 情景1：消费者订阅：相同交换机名称、路由Key相同，队列名称不同；生产者提交消息（交换机名称、路由key与消费者订阅时用的相同）；结果：消费者都接收到相同数量的消息；
        // 情景2：消费都订阅：相同交换机名称、路由key相同、队列名称相同；生产者提交消息（交换机名称、路由key与消费都订阅时用的相同）；结果：消息被均摊发送到消费者，起到负载均衡的作用；
        // 情景3：消费者订阅：相同交换机名称、路由key不同、队列名称不同；生产都提交消息（交换机名称与消费都订阅用的相同、路由key不同）；
        //       结果：交换机接收到消息后，根据routekey去模糊匹配队列，消费都接收到的消息数量不同；

        /*
            route key 通配符
            符号：“#” 匹配一个或者多个词
            符号：“*” 匹配不多不少一个词
        
            列如：
            “log.#” 能够匹配到 “log.info.oa”
            “log.*” 能够匹配到 “log.err”
         */

        // Fanout Exchange（输出交换机Fanout Exchange（不做路由），不处理路由键，只需要简单的将队列绑定到交换机上；）
        // 情景1：消费者订阅：相同交换机名称、队列名称不同；生产者提交消息（交换机名称与消费者订阅时用的相同）；结果：消费者都接收到相同数量的消息；
        // 情景2：消费者订阅：相同交换机名称、队列名称相同；生产者提交消息（交换机名称与消费者订阅时用的相同）；结果：消息被均摊发送到消费者，起到负载均衡的作用；



        // 消费者
        var consumers = new List<MyMqClient>();
        for (int i = 0; i < 3; i++)
        {
            var clientName = $"local-test-client-{i+1}";
            var client = new MyMqClient(host, port, userName, password, clientName);
            client.Connect();
            // queue = $"{queue}-{i+1}";
            // exchange = $"{exchange}-{i+1}";
            client.Subscribe(exchange, queue, routeKeys[i], true, (data, routingKey, exchange, consumerTag, orderRouteKey) =>
            {
                Console.WriteLine($"接收到订阅消息: orderRouteKey={orderRouteKey},routingKey={routingKey},exchange={exchange}," +
                    $"consumerTag={consumerTag},data={Encoding.UTF8.GetString(data)}\r\n");
                return true;
            }, ExchangeType.Topic);


            consumers.Add(client);
        }

        Thread.Sleep(2000);

        // 生产者
        var product = new MyMqClient(host, port, userName, password, $"local-test-product");
        product.Connect();
        // true时，交换器无法根据自身的类型和路由键找到一个符合条件的队列，消息返回给生产者；
        // false时，交换器无法根据自身的类型和路由键找到一个符合条件的队列，消息直接丢弃。RabbitMQ默认false
        var mandatory = false;
        // 消息未投递到队列，触发返回事件
        var returnEventCount = product.AddReturnEvent((exchange, routingKey, body) =>
        {
            Console.WriteLine($"消息未投递到队列，触发返回事件: exchange={exchange},routingKey={routingKey},body={Encoding.UTF8.GetString(body)}\r\n");
        });

        for (int i = 0; i < 6; i++)
        {
            var message = Encoding.UTF8.GetBytes($"-> Hello World {DateTime.Now}");

            // exchange = $"{exchange}-p";
            var newRoutingKey = $"notfound.local.test.msg";// pushRouteKeys[i];
            product.Publish(exchange, newRoutingKey, message, 
            ExchangeType.Topic, true, true, mandatory);
            
            Thread.Sleep(1000);
        }
        Console.ReadLine();
    }

    private static void Subscribe()
    {
        //string consumername1 = "consumer1", consumername2 = "consumer1";    //独立订阅

        string consumername1 = "consumer1", consumername2 = "consumer2";    //共同订阅
        Lails.MQClient.Base.Net6.MQClient consumer1 = CreateClient(consumername1);
        Lails.MQClient.Base.Net6.MQClient consumer2 = CreateClient(consumername2);

        consumer1.Connect();
        consumer1.Subscribe("msg.*", msg =>
        {
            Console.WriteLine($"订阅1接收消息：Topic={msg.Topic}, Data={Encoding.UTF8.GetString(msg.Data)}");
            consumer1.UnSubscribe("msg");
            Console.WriteLine("退订consumer1");
            return true; //ACK
        });

        consumer2.Connect();
        consumer2.Subscribe("Kw-NB-IoT.DoorSensor.AlarmRelieved", msg =>
        {
            Console.WriteLine($"订阅2接收消息：Topic={msg.Topic}, Data={Encoding.UTF8.GetString(msg.Data)}");
            // consumer2.UnSubscribe("msg");
            Console.WriteLine("退订consumer2");
            return true; //ACK
        });
    }

    private static void Publish()
    {
        Lails.MQClient.Base.Net6.MQClient publisher = CreateClient("publisher");
        publisher.Connect();
        string message = $"Rabbitmq Publish Message {DateTime.Now.ToString()}";
        byte[] body1 = Encoding.UTF8.GetBytes(message + "_01");
        byte[] body2 = Encoding.UTF8.GetBytes(message + "_02");
        publisher.Publish("msg.a", body1);
        publisher.Publish("msg.b", body2);
        Console.WriteLine($"发送消息：{message}");
    }

    private static Lails.MQClient.Base.Net6.MQClient CreateClient(string clientID)
    {
        //MQClient client = new RabbitMQClient("mq.lails.cc", "demo", "demo", clientID, appid: "demo");
        var client = new RabbitMQClient("127.0.0.1:5672", "test", "test", clientID, "demo");
        return client;
    }
}